package main

import (
	"context"
	"fmt"
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/yaml.v3"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"
)

//测试造数据源
func sedMes() {
	for i:=0; i<120100; i++ {
		 mesChan <- &EsStruct{
			 Time: time.Now().UTC().Format(time.RFC3339),
			 Address: "上海",
			 Devicetype: "CDN-服务器",
			 Devicename: "缓存-linux-01",
			 Datalog: fmt.Sprintf("%d-hello world", i),
		 }
	}
}

//单条一次一次落库ES
func saveEsOne(ctx context.Context) {
	for {
		select {
		case str := <- mesChan:
			AddDoc(aliasIndex, str)
		case <- ctx.Done():
			return
		}
	}
}

//启用批量落库函数
func saveESRoute(ctx context.Context) {
	outTime := time.Duration(FileNames.EsPerformance.BufferTimeout*1000*1000*1000)
	defer close(saveEs)
	for {
		select {
		case bufIdx := <- saveEs:
			ctxDeadline, cancelDeadline := context.WithDeadline(context.Background(), time.Now().Add(outTime))
			go revEsMes(bufIdx, &ctxDeadline, &cancelDeadline)
		case <- ctx.Done():
			return
		}
	}
}
func revEsMes(bufIdx int, ctxDeadline *context.Context, cancelDeadline *context.CancelFunc) {
	for {
		select {
		case str := <- mesChan:
			if nIdx[bufIdx] >= FileNames.EsPerformance.BufferSize {
				sugarLogger.Infof("buffer[%d]槽已满 %d 条数据落库", bufIdx, nIdx[bufIdx])
				AddDocBulk(aliasIndex, buffer[bufIdx][:nIdx[bufIdx]])
				buffer[bufIdx][0], nIdx[bufIdx] = str, 1
				return
			}
			buffer[bufIdx][nIdx[bufIdx]] = str; nIdx[bufIdx]++
		case <-(*ctxDeadline).Done():
			if nIdx[bufIdx] != 0 {
				sugarLogger.Infof("buffer[%d]槽 %d 条数据落库", bufIdx, nIdx[bufIdx])
				AddDocBulk(aliasIndex, buffer[bufIdx][:nIdx[bufIdx]])
				nIdx[bufIdx] = 0
			}
			(*cancelDeadline)()
			sugarLogger.Infof("Deadline buffer[%d]槽 over ---> exit", bufIdx)
			saveEs <- bufIdx
			return
		}
	}
}

/*
	清洗包含通配符的文件, 递归遍历所有文件
	贪心算法进行通配符匹配,符合的加入到list里
 */
func MachFile(path string, list *[]string) {
	idx := strings.LastIndexFunc(path, func(r rune) bool{
		return r == '\\' || r == '/'
	})
	//如果包含通配符则进行通配符匹配, 否则直接返回
	if strings.ContainsAny(path[idx+1:], "*?") {
		ListMachFile(path[:idx], path[idx+1:], list)
	} else {
		*list = append(*list, path)
	}
}
func ListMachFile(dirPth, wildcard string, list *[]string){
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		sugarLogger.Errorf("读取目录: %s 失败, 失败原因: %s", dirPth, err)
	}
	PthSep := string(os.PathSeparator)
	for _, fi := range dir {
		if fi.IsDir() {
			ListMachFile(dirPth + PthSep + fi.Name(), wildcard, list)
		} else if isMatch(fi.Name(), wildcard) {
			*list = append(*list, dirPth+PthSep+fi.Name())
		}
	}
}
//贪心算法 通配符匹配
func isMatch(s string, p string) bool {
	for len(s) > 0 && len(p) > 0 && p[len(p)-1] != '*' {
		if charMatch(s[len(s)-1], p[len(p)-1]) {
			s = s[:len(s)-1]
			p = p[:len(p)-1]
		} else {
			return false
		}
	}
	if len(p) == 0 {
		return len(s) == 0
	}
	sIndex, pIndex := 0, 0
	sRecord, pRecord := -1, -1
	for sIndex < len(s) && pRecord < len(p) {
		if p[pIndex] == '*' {
			pIndex++
			sRecord, pRecord = sIndex, pIndex
		} else if charMatch(s[sIndex], p[pIndex]) {
			sIndex++
			pIndex++
		} else if sRecord != -1 && sRecord + 1 < len(s) {
			sRecord++
			sIndex, pIndex = sRecord, pRecord
		} else {
			return false
		}
	}
	return allStars(p, pIndex, len(p))
}
func allStars(str string, left, right int) bool {
	for i := left; i < right; i++ {
		if str[i] != '*' {
			return false
		}
	}
	return true
}
func charMatch(u, v byte) bool {
	return u == v || v == '?'
}

//读取yaml配置文件函数
func ReadYaml(FileName *Resource) {
	yamlFile, err := ioutil.ReadFile("/etc/monitorFile.yaml")
	if err != nil {
		fmt.Printf("failed to read yaml file : %v\n", err)
		os.Exit(1)
	}
	err = yaml.Unmarshal(yamlFile, FileName)
	if err != nil {
		fmt.Printf("failed to unmarshal : %v\n", err)
		os.Exit(2)
	}
}

//获取天气函数
func getTian(){
	Tianqi, _ = getWeather(FileNames.WeatherCode)
	sugarLogger.Debugf("%s当前天气为: %s", FileNames.AddressWeather, Tianqi)
	if strings.Contains(Tianqi, "雷") {
		Tianqi = "⛈"
	} else if strings.Contains(Tianqi, "雨") {
		Tianqi = "🌧"
	} else if strings.Contains(Tianqi, "雪") {
		Tianqi = "🌨"
	} else if strings.Contains(Tianqi, "云") {
		Tianqi = "🌤"
	} else if strings.Contains(Tianqi, "阴") {
		Tianqi = "☁"
	} else if strings.Contains(Tianqi, "晴") {
		if time.Now().Hour() > 06 && time.Now().Hour() < 20 {
			Tianqi = "☀"
		} else {
			Tianqi = "🌙"
		}
	} else {
		Tianqi = TianqiBak
	}
	TianqiBak = Tianqi
}
func doHttpGetRequest(url string) (rlt string,err error)  {
	resp,err := http.Get(url)
	if err!=nil {
		return "",err
	} else {
		defer resp.Body.Close()
		body,err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return "",err
		} else {
			return string(body), err
		}
	}
}
func getWeather(tian string) (string,error) {
	url := "https://restapi.amap.com/v3/weather/weatherInfo?"
	key := "9133bed38c383aec2302abe93fc88e4d"
	city := tian
	ext := "all"
	rlt,err:=doHttpGetRequest(url + "city=" + city + "&key=" + key + "&extensions=" + ext)
	var data Weather
	var fore Forecast
	var cast Cast
	json.Unmarshal([]byte(rlt),&data)
	if err != nil {
		sugarLogger.Errorf("天气获取失败: %s", err.Error())
		return err.Error(),err
	} else {
		fore = data.Forecasts[0]
		var str string
		cast = fore.Casts[0]
		if time.Now().Hour() > 06 && time.Now().Hour() < 20 {
			str = cast.Dayweather
		} else {
			str = cast.Nightweather
		}
		return str,nil
	}
}

//初始化日志
func InitLogger() {
	writeSyncer := getLogWriter()
	encoder := getEncoder()
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
	logger := zap.New(core, zap.AddCaller())
	sugarLogger = logger.Sugar()
}

func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	return zapcore.NewConsoleEncoder(encoderConfig)
}

func getLogWriter() zapcore.WriteSyncer {
	lumberJackLogger := &lumberjack.Logger{
		Filename:   FileNames.OutConfigLog.LogName,
		MaxSize:    FileNames.OutConfigLog.MaxSize,
		MaxBackups: FileNames.OutConfigLog.MaxBackups,
		MaxAge:     FileNames.OutConfigLog.MaxAges,
		Compress:   FileNames.OutConfigLog.Compress,
	}
	return zapcore.AddSync(lumberJackLogger)
}

